package com.nyc.springspi;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.io.UrlResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URL;
import java.util.*;

/**
 * @author niuyicong
 * @date 2025/9/30  14:53:29
 * @description
 */
public class SpringSPIFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/myspring.factories";

    private static final Log logger = LogFactory.getLog(org.springframework.core.io.support.SpringFactoriesLoader.class);

    private SpringSPIFactoriesLoader() {}

    private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();


    public static List<String> myLoadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        String factoryTypeName = factoryType.getName();
        return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
        // 尝试从缓存中获取已加载过的结果
        MultiValueMap<String, String> result = cache.get(classLoader);
        if (result != null) {
            return result;
        }
        // 加载所有 classpath 下 META-INF/myspring.factories 资源文件
        try {
            Enumeration<URL> urls = (classLoader != null ?
                    classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                    ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
            // 初始化存储结果的结构 LinkedMultiValueMap这个结构可以给一个key存放多个value，并且可以保持key的顺序
            result = new LinkedMultiValueMap<>();
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                // 将 URL 封装为 Spring 的 Resource
                UrlResource resource = new UrlResource(url);
                // 加载 Properties 配置（key=value）
                Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                // 遍历每一条配置项
                for (Map.Entry<?, ?> entry : properties.entrySet()) {
                    // key = 工厂接口类名（字符串形式）
                    String factoryTypeName = ((String) entry.getKey()).trim();
                    // value 可能是多个实现类，用逗号隔开，需要拆分成字符串数组
                    for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
                        // 存入结果：一个接口可能对应多个实现类
                        result.add(factoryTypeName, factoryImplementationName.trim());
                    }
                }
            }
            cache.put(classLoader, result);
            return result;
        }
        catch (IOException ex) {
            throw new IllegalArgumentException("Unable to load factories from location [" +
                    FACTORIES_RESOURCE_LOCATION + "]", ex);
        }
    }
}
